package com.googlecode.javarpctp.test.common;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;
import javax.swing.event.EventListenerList;

public class ThreadMonitor extends Thread {

    protected EventListenerList listenerList = new EventListenerList();
    private ConcurrentHashMap<Long, MonitoredThreadState> monitoredThreadsState = new ConcurrentHashMap<Long, MonitoredThreadState>();
    private long cleanupInterval = 15000;
    private long lastCleanup = 0;

    public ThreadMonitor() {        
        this.lastCleanup = System.currentTimeMillis();
    }

    public ThreadMonitor(long cleanupInterval) {
        this();
        this.cleanupInterval = cleanupInterval;
    }

    @Override
    public void run() {
        this.monitoredThreadsState.put(this.getId(), new MonitoredThreadState(this, this.getState()));
        while (true) {
            try {
                boolean removeEndedThreads = false;
                ArrayList<Long> threadsToRemove = new ArrayList<Long>();
                if ((System.currentTimeMillis() - lastCleanup) > cleanupInterval) {
                    removeEndedThreads = true;
                }
                Enumeration<MonitoredThreadState> enumerator = monitoredThreadsState.elements();
                while (enumerator.hasMoreElements()) {
                    MonitoredThreadState state = enumerator.nextElement();
                    if (state.getThread().getState() != state.getLastState()) {
                        this.fireThreadStateChanged(new ThreadStateChangedEvent(this, state.getThread(), state.getLastState(), state.getThread().getState()));
                        state.setLastState(state.getThread().getState());
                    }
                    if (state.getThread().getState() == Thread.State.TERMINATED && removeEndedThreads) {
                        threadsToRemove.add(state.getThread().getId());
                    }
                }

                if (removeEndedThreads) {                    
                    for (Long threadID : threadsToRemove) {                        
                        monitoredThreadsState.remove(threadID);
                    }
                    lastCleanup = System.currentTimeMillis();
                }

                Thread.sleep(500);
            } catch (Exception ex) {
                /* Do nothing */
            }
        }
    }

    public void addThreadToMonitor(Thread thread) {
        this.monitoredThreadsState.put(thread.getId(), new MonitoredThreadState(thread, thread.getState()));
        this.fireThreadStateChanged(new ThreadStateChangedEvent(this, thread, thread.getState(), thread.getState()));
    }

    // <editor-fold defaultstate="collapsed" desc="Events">
    public void addThreadMonitorListener(ThreadMonitorListener listener) {
        this.listenerList.add(ThreadMonitorListener.class, listener);
    }
    
    public void removeThreadMonitorListener(ThreadMonitorListener listener) {
        this.listenerList.remove(ThreadMonitorListener.class, listener);
    }
    
    void fireThreadStateChanged(ThreadStateChangedEvent evt) {
        Object[] listeners = this.listenerList.getListenerList();
        // Each listener occupies two elements - the first is the listener class
        // and the second is the listener instance
        for (int i = 0; i < listeners.length; i += 2) {
            if (listeners[i] == ThreadMonitorListener.class) {
                ((ThreadMonitorListener) listeners[i + 1]).threadStateChanged(evt);
            }
        }
    }
    // </editor-fold>
}
